package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Dynamic Proxy Pattern example
 * with AOP Pattern, Simple Factory Pattern, Template Pattern, Strategy Pattern?
 * @author JUNYONZH
 *
 */
public class DynaProxy {

	public static void main(String[] args) {
		Hello hello = (Hello)DynaProxy.getHelloInstance();
		System.out.println(hello.sayHello());
	}
	
	//This is a Simple Factory Pattern
	public static Object getHelloInstance() {
		return Proxy.newProxyInstance(Hello.class
				.getClassLoader(), new Class[] { Hello.class },
				new Handle());
	}

}

interface Hello {
	public String sayHello();
}

class HelloImp implements Hello {
	public String sayHello() {
		System.out.println("In method sayHello");
		return "Hello World!";
	}
}

interface BeforeInvocation {
	public void before(Object obj, Method meth, Object[] args);
}

interface AfterInvocation {
	public void after(Object obj, Method meth, Object[] args);
}

interface CrossInvocation {
	public Object cross(Object obj, Method meth, Object[] args);
}

class Handle implements InvocationHandler {
	
	private Object instance;
	private BeforeInvocation before = null;
	private AfterInvocation after = null;
	private CrossInvocation cross = new CrossInvocation() {
		public Object cross(Object obj, Method meth, Object[] args) {
			Object result = null;
			try {
				result = meth.invoke(obj, args);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return result;
		}
	};
	
	public Handle() {
		//Replace the instance which need to be proxyed
		instance = new HelloImp();
		
		//Replace the class which implemented BeforeInvocation
//		before = new BeforeInvocation() {
//			public void before(Object obj, Method meth, Object[] args) {
//				System.out.println("Begin invoke " + meth.getName());
//			}
//		};
		
		//Replace the class which implemented AfterInvocation
//		after = new AfterInvocation() {
//			public void after(Object obj, Method meth, Object[] args) {
//				System.out.println("End invoke " + meth.getName());
//			}
//		};
		
		//Replace the class which implemented CrossInvocation
//		cross = new CrossInvocation() {
//			public Object cross(Object obj, Method meth, Object[] args) {
//				Object result = null;
//				try {
//					result = meth.invoke(obj, args);
//				} catch (IllegalArgumentException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				} catch (IllegalAccessException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				} catch (InvocationTargetException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				result = "ZJY said: '" + result + "'";
//				return result;
//			}
//		};
	}
	
	//This is a Template Pattern?
	public Object invoke(Object obj, Method meth, Object[] args) {
		if(before != null) {
			before.before(instance, meth, args);
		}
		Object result = cross.cross(instance, meth, args);		
		if(after != null) {
			after.after(instance, meth, args);
		}
		return result;
	}
}
